// 活体检测工具库（WSC版本）
import * as faceapi from '@vladmandic/face-api';

// 头部姿态检测相关类型
export interface HeadPoseResult {
  yaw: number;
  pitch: number;
  roll: number;
}

// 活体检测状态类型
export type LivenessState =
  | 'idle'
  | 'headshake'
  | 'mouthopen'
  | 'blink'
  | 'speech'
  | 'completed';

// 语音识别相关类型
export interface SpeechRecognitionResult {
  transcript: string;
  confidence: number;
}

// 计算头部姿态角函数
export const calculateHeadPose = (
  landmarks: faceapi.FaceLandmarks68
): HeadPoseResult => {
  // 获取关键点
  const nose = landmarks.getNose();
  // const _jawline = landmarks.getJawOutline();
  const leftEye = landmarks.getLeftEye();
  const rightEye = landmarks.getRightEye();

  // 计算鼻尖和两眼中心点
  const noseTip = nose[3];
  const leftEyeCenter = {
    x:
      leftEye.reduce((sum: number, point: { x: number }) => sum + point.x, 0) /
      leftEye.length,
    y:
      leftEye.reduce((sum: number, point: { y: number }) => sum + point.y, 0) /
      leftEye.length,
  };
  const rightEyeCenter = {
    x:
      rightEye.reduce((sum: number, point: { x: number }) => sum + point.x, 0) /
      rightEye.length,
    y:
      rightEye.reduce((sum: number, point: { y: number }) => sum + point.y, 0) /
      rightEye.length,
  };

  // 计算眼睛中心点
  const eyesCenter = {
    x: (leftEyeCenter.x + rightEyeCenter.x) / 2,
    y: (leftEyeCenter.y + rightEyeCenter.y) / 2,
  };

  // 计算眼睛距离（用于缩放）
  const eyeDistance = Math.sqrt(
    Math.pow(rightEyeCenter.x - leftEyeCenter.x, 2) +
      Math.pow(rightEyeCenter.y - leftEyeCenter.y, 2)
  );

  // 计算左右摇头角度 (Yaw)
  // 当头向右转时，鼻尖相对于眼睛中心点向右移动，得到负值；向左转时得到正值
  const yaw =
    Math.atan2(noseTip.x - eyesCenter.x, eyeDistance) * (180 / Math.PI);

  // 计算上下点头角度 (Pitch)
  const pitch =
    Math.atan2(eyesCenter.y - noseTip.y, eyeDistance) * (180 / Math.PI);

  // 计算头部倾斜角度 (Roll)
  const roll =
    Math.atan2(
      rightEyeCenter.y - leftEyeCenter.y,
      rightEyeCenter.x - leftEyeCenter.x
    ) *
    (180 / Math.PI);

  return { yaw, pitch, roll };
};

// 计算嘴部开合比率函数
export const calculateMouthOpenRatio = (
  landmarks: faceapi.FaceLandmarks68
): number => {
  // 获取嘴部特征点（索引48-67）
  const mouth = landmarks.getMouth();

  // 计算嘴部宽度（左右两边的点）
  const mouthWidth = Math.sqrt(
    Math.pow(mouth[0].x - mouth[6].x, 2) + Math.pow(mouth[0].y - mouth[6].y, 2)
  );

  // 计算嘴部高度（上下两边的点）
  // const _mouthHeight = Math.sqrt(
  //   Math.pow(mouth[3].x - mouth[9].x, 2) +
  //   Math.pow(mouth[3].y - mouth[9].y, 2)
  // );

  // 计算内嘴高度
  const innerMouthHeight = Math.sqrt(
    Math.pow(mouth[13].x - mouth[19].x, 2) +
      Math.pow(mouth[13].y - mouth[19].y, 2)
  );

  // 计算高度与宽度的比值
  const ratio = innerMouthHeight / mouthWidth;

  return ratio;
};

// 计算眼睛纵横比函数（用于眨眼检测）
export const calculateEyeAspectRatio = (
  landmarks: faceapi.FaceLandmarks68
): number => {
  // 获取左右眼的特征点
  const leftEye = landmarks.getLeftEye();
  const rightEye = landmarks.getRightEye();

  // 计算左眼纵横比
  // 眼睛垂直距离（上下眼睑间距离）
  const leftEyeVertical1 = Math.sqrt(
    Math.pow(leftEye[1].x - leftEye[5].x, 2) +
      Math.pow(leftEye[1].y - leftEye[5].y, 2)
  );
  const leftEyeVertical2 = Math.sqrt(
    Math.pow(leftEye[2].x - leftEye[4].x, 2) +
      Math.pow(leftEye[2].y - leftEye[4].y, 2)
  );
  // 眼睛水平距离（眼角间距离）
  const leftEyeHorizontal = Math.sqrt(
    Math.pow(leftEye[0].x - leftEye[3].x, 2) +
      Math.pow(leftEye[0].y - leftEye[3].y, 2)
  );
  const leftEAR =
    (leftEyeVertical1 + leftEyeVertical2) / (2 * leftEyeHorizontal);

  // 计算右眼纵横比
  const rightEyeVertical1 = Math.sqrt(
    Math.pow(rightEye[1].x - rightEye[5].x, 2) +
      Math.pow(rightEye[1].y - rightEye[5].y, 2)
  );
  const rightEyeVertical2 = Math.sqrt(
    Math.pow(rightEye[2].x - rightEye[4].x, 2) +
      Math.pow(rightEye[2].y - rightEye[4].y, 2)
  );
  const rightEyeHorizontal = Math.sqrt(
    Math.pow(rightEye[0].x - rightEye[3].x, 2) +
      Math.pow(rightEye[0].y - rightEye[3].y, 2)
  );
  const rightEAR =
    (rightEyeVertical1 + rightEyeVertical2) / (2 * rightEyeHorizontal);

  // 返回两眼纵横比的平均值
  return (leftEAR + rightEAR) / 2;
};

// 语音识别相关类型声明
interface SpeechRecognitionEvent {
  results: SpeechRecognitionResultList;
}

interface SpeechRecognitionErrorEvent {
  error: string;
}

interface WebkitSpeechRecognition {
  lang: string;
  continuous: boolean;
  interimResults: boolean;
  maxAlternatives: number;
  timeout?: number;
  maxSpeechLength?: number;
  serviceURI?: string;
  onresult: (event: SpeechRecognitionEvent) => void;
  onend: () => void;
  onerror: (event: SpeechRecognitionErrorEvent) => void;
  start: () => void;
  stop: () => void;
  abort: () => void;
}

declare global {
  interface Window {
    SpeechRecognition?: typeof WebkitSpeechRecognition;
    webkitSpeechRecognition?: typeof WebkitSpeechRecognition;
  }
}

// 语音识别工具类
export class SpeechRecognitionHelper {
  private recognition: WebkitSpeechRecognition | null = null;
  private isListening = false;
  private retryCount = 0;
  private maxRetries = 2;
  private retryTimeout: number | null = null;

  constructor() {
    this.initRecognition();
  }

  private initRecognition() {
    const SpeechRecognitionAPI =
      window.SpeechRecognition || window.webkitSpeechRecognition;

    if (!SpeechRecognitionAPI) {
      throw new Error('浏览器不支持语音识别API');
    }

    this.recognition = new SpeechRecognitionAPI();
    this.recognition.lang = 'zh-CN';
    this.recognition.continuous = false;
    this.recognition.interimResults = false;
    this.recognition.maxAlternatives = 1;

    // 添加超时设置
    this.recognition.timeout = 10000; // 10秒超时
    this.recognition.maxSpeechLength = 5000; // 5秒最大语音长度

    // 尝试使用本地识别（如果支持）
    try {
      this.recognition.serviceURI = undefined;
    } catch (e) {
      console.warn('无法设置语音识别服务URI:', e);
    }
  }

  startListening(
    onResult: (result: SpeechRecognitionResult) => void,
    onEnd: () => void,
    onError: (error: string) => void
  ) {
    if (!this.recognition || this.isListening) return;

    // 重置重试计数
    this.retryCount = 0;

    this.recognition.onresult = (event: SpeechRecognitionEvent) => {
      if (!event.results || event.results.length === 0) return;

      const result = event.results[event.results.length - 1];
      if (result && result.length > 0) {
        const transcript = result[0].transcript;
        const confidence = result[0].confidence;

        onResult({
          transcript: transcript,
          confidence: confidence,
        });
      }
    };

    this.recognition.onend = () => {
      this.isListening = false;
      onEnd();
    };

    this.recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
      console.error('语音识别错误:', event.error);
      this.isListening = false;

      // 根据错误类型提供更友好的处理
      let errorMessage = event.error;
      let shouldRetry = false;

      if (event.error === 'network') {
        errorMessage = '网络连接问题，正在重试...';
        shouldRetry = this.retryCount < 1; // 网络错误只重试1次
      } else if (event.error === 'not-allowed') {
        errorMessage = '麦克风权限被拒绝，请允许麦克风访问';
        shouldRetry = false;
      } else if (event.error === 'no-speech') {
        errorMessage = '未检测到语音，正在重试...';
        shouldRetry = true;
      } else if (event.error === 'audio-capture') {
        errorMessage = '无法访问麦克风，请检查设备';
        shouldRetry = false;
      } else if (event.error === 'aborted') {
        errorMessage = '语音识别被中断，正在重试...';
        shouldRetry = true;
      }

      // 如果应该重试且未超过最大重试次数
      if (shouldRetry && this.retryCount < this.maxRetries) {
        this.retryCount++;
        console.warn(`语音识别重试 ${this.retryCount}/${this.maxRetries}`);

        // 延迟重试，网络错误使用更长的延迟
        const delay =
          event.error === 'network'
            ? 2000 * this.retryCount
            : 1000 * this.retryCount;
        this.retryTimeout = window.setTimeout(() => {
          // 重新初始化识别器
          try {
            this.initRecognition();
            this.startListening(onResult, onEnd, onError);
          } catch (initError) {
            console.error('重新初始化语音识别失败:', initError);
            onError('语音识别服务不可用，请刷新页面重试');
          }
        }, delay) as number;
      } else {
        // 重试次数用完或不可重试的错误
        if (this.retryCount >= this.maxRetries) {
          errorMessage = '语音识别多次失败，建议跳过语音验证或刷新页面重试';
        }
        onError(errorMessage);
      }
    };

    try {
      // 添加网络状态检查
      if (!navigator.onLine) {
        onError('网络连接已断开，请检查网络连接');
        return;
      }

      this.recognition.start();
      this.isListening = true;
    } catch (error) {
      console.error('启动语音识别失败:', error);
      onError('启动语音识别失败，请重试');
    }
  }

  stopListening() {
    // 清除重试定时器
    if (this.retryTimeout) {
      clearTimeout(this.retryTimeout);
      this.retryTimeout = null;
    }

    if (this.recognition && this.isListening) {
      try {
        this.recognition.stop();
      } catch (error) {
        console.warn('停止语音识别时发生错误:', error);
      }
      this.isListening = false;
    }
  }

  abort() {
    // 清除重试定时器
    if (this.retryTimeout) {
      clearTimeout(this.retryTimeout);
      this.retryTimeout = null;
    }

    if (this.recognition) {
      try {
        this.recognition.abort();
      } catch (error) {
        console.warn('中止语音识别时发生错误:', error);
      }
      this.isListening = false;
    }
  }
}

// 活体检测管理器
export class LivenessDetector {
  private video: HTMLVideoElement;
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private intervalId: number | null = null;
  private isDetecting = false;

  constructor(video: HTMLVideoElement, canvas: HTMLCanvasElement) {
    this.video = video;
    this.canvas = canvas;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    if (!ctx) {
      throw new Error('无法获取Canvas 2D上下文');
    }
    this.ctx = ctx;
  }

  // 启动头部姿态检测
  async startHeadShakeDetection(
    onProgress: (progress: number, yaw: number) => void,
    onComplete: () => void,
    onTimeout: () => void
  ) {
    if (this.isDetecting) return;

    this.isDetecting = true;
    const yawAngles: number[] = [];
    let maxYawLeft = 0;
    let maxYawRight = 0;

    // 设置15秒超时
    const timeoutId = setTimeout(() => {
      if (this.isDetecting) {
        this.isDetecting = false;
        onTimeout();
      }
    }, 15000);

    this.intervalId = window.setInterval(async () => {
      if (!this.isDetecting) return;

      try {
        const detections = await faceapi
          .detectAllFaces(this.video, new faceapi.TinyFaceDetectorOptions())
          .withFaceLandmarks();

        if (detections && detections.length > 0) {
          const faceWithLandmarks = detections[0];
          const landmarks = faceWithLandmarks.landmarks;

          const { yaw } = calculateHeadPose(landmarks);
          yawAngles.push(yaw);

          if (yaw > maxYawLeft) maxYawLeft = yaw;
          if (yaw < maxYawRight) maxYawRight = yaw;

          const yawDifference = maxYawLeft - maxYawRight;
          const threshold = 58;
          const progress = Math.min(100, (yawDifference / threshold) * 100);

          onProgress(progress, yaw);

          if (yawDifference >= threshold) {
            this.isDetecting = false;
            clearTimeout(timeoutId);
            if (this.intervalId) {
              clearInterval(this.intervalId);
              this.intervalId = null;
            }
            onComplete();
          }
        }
      } catch (error) {
        console.error('头部姿态检测出错:', error);
      }
    }, 200);
  }

  // 启动嘴部开合检测
  async startMouthOpenDetection(
    onProgress: (progress: number, ratio: number) => void,
    onComplete: () => void,
    onTimeout: () => void
  ) {
    if (this.isDetecting) return;

    this.isDetecting = true;
    let maxMouthOpenRatio = 0;

    const timeoutId = setTimeout(() => {
      if (this.isDetecting) {
        this.isDetecting = false;
        onTimeout();
      }
    }, 15000);

    this.intervalId = window.setInterval(async () => {
      if (!this.isDetecting) return;

      try {
        const detections = await faceapi
          .detectAllFaces(this.video, new faceapi.TinyFaceDetectorOptions())
          .withFaceLandmarks();

        if (detections && detections.length > 0) {
          const faceWithLandmarks = detections[0];
          const landmarks = faceWithLandmarks.landmarks;

          const ratio = calculateMouthOpenRatio(landmarks);
          if (ratio > maxMouthOpenRatio) {
            maxMouthOpenRatio = ratio;
          }

          const threshold = 0.6;
          const progress = Math.min(100, (maxMouthOpenRatio / threshold) * 100);

          onProgress(progress, ratio);

          if (maxMouthOpenRatio >= threshold) {
            this.isDetecting = false;
            clearTimeout(timeoutId);
            if (this.intervalId) {
              clearInterval(this.intervalId);
              this.intervalId = null;
            }
            onComplete();
          }
        }
      } catch (error) {
        console.error('嘴部检测出错:', error);
      }
    }, 200);
  }

  // 启动眨眼检测
  async startBlinkDetection(
    onProgress: (progress: number, count: number, ear: number) => void,
    onComplete: () => void,
    onTimeout: () => void
  ) {
    if (this.isDetecting) return;

    this.isDetecting = true;
    let blinkCount = 0;
    let lastBlinkTime = Date.now();
    let eyeClosed = false;

    const timeoutId = setTimeout(() => {
      if (this.isDetecting) {
        this.isDetecting = false;
        onTimeout();
      }
    }, 15000);

    this.intervalId = window.setInterval(async () => {
      if (!this.isDetecting) return;

      try {
        const detections = await faceapi
          .detectAllFaces(this.video, new faceapi.TinyFaceDetectorOptions())
          .withFaceLandmarks();

        if (detections && detections.length > 0) {
          const faceWithLandmarks = detections[0];
          const landmarks = faceWithLandmarks.landmarks;

          const ear = calculateEyeAspectRatio(landmarks);
          const eyeClosedThreshold = 0.28;
          const eyeOpenThreshold = 0.3;
          const blinkTarget = 5;
          const now = Date.now();

          if (!eyeClosed && ear < eyeClosedThreshold) {
            eyeClosed = true;
          } else if (eyeClosed && ear > eyeOpenThreshold) {
            eyeClosed = false;
            if (now - lastBlinkTime > 500) {
              blinkCount++;
              lastBlinkTime = now;
            }
          }

          const progress = Math.min(100, (blinkCount / blinkTarget) * 100);
          onProgress(progress, blinkCount, ear);

          if (blinkCount >= blinkTarget) {
            this.isDetecting = false;
            clearTimeout(timeoutId);
            if (this.intervalId) {
              clearInterval(this.intervalId);
              this.intervalId = null;
            }
            onComplete();
          }
        }
      } catch (error) {
        console.error('眨眼检测出错:', error);
      }
    }, 50);
  }

  // 停止检测
  stop() {
    this.isDetecting = false;
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  // 清理资源
  dispose() {
    this.stop();
    if (this.video.srcObject) {
      const stream = this.video.srcObject as MediaStream;
      stream.getTracks().forEach(track => track.stop());
      this.video.srcObject = null;
    }
  }
}
